from sqlalchemy.orm import Session
from sqlalchemy import and_, func
from typing import List, Optional, Tuple, Dict
from datetime import datetime
from fastapi import HTTPException
from ..models.models import Student, Room, SwapRequest, Building
from ..config import settings
from sqlalchemy.exc import SQLAlchemyError
import logging

logger = logging.getLogger(__name__)

class DormitoryAssignmentService:
    def __init__(self, db: Session):
        self.db = db
        
    def get_available_rooms(
        self,
        gender: str,
        required_capacity: int = 1,
        preferred_floor: Optional[int] = None,
        preferred_building: Optional[int] = None
    ) -> List[Room]:
        """
        获取可用的宿舍房间
        
        参数:
            gender: 性别要求
            required_capacity: 需要的床位数
            preferred_floor: 偏好的楼层
            preferred_building: 偏好的楼栋ID
        """
        try:
            query = (
                self.db.query(Room)
                .join(Building)
                .filter(
                    Building.gender == gender,
                    Room.capacity - Room.current_occupancy >= required_capacity
                )
            )
            
            if preferred_floor:
                query = query.filter(Room.floor == preferred_floor)
            if preferred_building:
                query = query.filter(Building.id == preferred_building)
                
            return query.order_by(Room.floor, Room.room_number).all()
        except SQLAlchemyError as e:
            logger.error(f"查询可用房间时发生错误: {str(e)}")
            raise HTTPException(status_code=500, detail="查询房间失败")
        
    def assign_room(self, student: Student, room: Room) -> bool:
        """
        将学生分配到指定宿舍
        
        参数:
            student: 待分配的学生
            room: 目标房间
        """
        try:
            # 检查房间容量
            if room.current_occupancy >= room.capacity:
                raise HTTPException(status_code=400, detail="房间已满")
                
            # 检查性别匹配
            building = self.db.query(Building).filter(Building.id == room.building_id).first()
            if not building or building.gender != student.gender:
                raise HTTPException(status_code=400, detail="性别不匹配")
                
            # 检查学生当前状态
            if student.room_id:
                raise HTTPException(status_code=400, detail="学生已有宿舍分配")
                
            # 分配房间
            student.room_id = room.id
            room.current_occupancy += 1
            
            self.db.commit()
            return True
            
        except SQLAlchemyError as e:
            self.db.rollback()
            logger.error(f"分配房间时发生错误: {str(e)}")
            raise HTTPException(status_code=500, detail="分配房间失败")
        
    def assign_group_to_room(self, students: List[Student], room: Room) -> bool:
        """
        将一组学生分配到同一个宿舍
        
        参数:
            students: 待分配的学生列表
            room: 目标房间
        """
        try:
            # 检查房间容量
            if len(students) > (room.capacity - room.current_occupancy):
                raise HTTPException(status_code=400, detail="房间剩余床位不足")
                
            # 检查性别匹配
            building = self.db.query(Building).filter(Building.id == room.building_id).first()
            if not building or any(s.gender != building.gender for s in students):
                raise HTTPException(status_code=400, detail="性别不匹配")
                
            # 检查学生当前状态
            if any(s.room_id for s in students):
                raise HTTPException(status_code=400, detail="部分学生已有宿舍分配")
                
            # 分配房间
            for student in students:
                student.room_id = room.id
            room.current_occupancy += len(students)
            
            self.db.commit()
            return True
            
        except SQLAlchemyError as e:
            self.db.rollback()
            logger.error(f"分配群组房间时发生错误: {str(e)}")
            raise HTTPException(status_code=500, detail="分配房间失败")
        
    def remove_from_room(self, student: Student) -> bool:
        """
        将学生从当前宿舍中移除
        
        参数:
            student: 要移除的学生
        """
        try:
            if student.room_id is None:
                raise HTTPException(status_code=400, detail="学生当前没有宿舍分配")
                
            room = self.db.query(Room).filter(Room.id == student.room_id).first()
            if not room:
                raise HTTPException(status_code=404, detail="找不到学生的宿舍信息")
                
            room.current_occupancy -= 1
            student.room_id = None
            
            self.db.commit()
            return True
            
        except SQLAlchemyError as e:
            self.db.rollback()
            logger.error(f"移除学生宿舍分配时发生错误: {str(e)}")
            raise HTTPException(status_code=500, detail="移除宿舍分配失败")
        
    def swap_rooms(self, student1: Student, student2: Student) -> bool:
        """
        交换两个学生的宿舍
        
        参数:
            student1: 第一个学生
            student2: 第二个学生
        """
        try:
            # 检查性别
            if student1.gender != student2.gender:
                raise HTTPException(status_code=400, detail="不同性别学生不能交换宿舍")
                
            # 检查当前宿舍分配
            if not (student1.room_id and student2.room_id):
                raise HTTPException(status_code=400, detail="部分学生没有宿舍分配")
                
            # 交换宿舍
            room1_id = student1.room_id
            room2_id = student2.room_id
            
            student1.room_id = room2_id
            student2.room_id = room1_id
            
            self.db.commit()
            return True
            
        except SQLAlchemyError as e:
            self.db.rollback()
            logger.error(f"交换宿舍时发生错误: {str(e)}")
            raise HTTPException(status_code=500, detail="交换宿舍失败")
        
    def get_room_occupants(self, room_id: int) -> List[Student]:
        """
        获取指定宿舍的所有住宿学生
        
        参数:
            room_id: 房间ID
        """
        try:
            return (
                self.db.query(Student)
                .filter(Student.room_id == room_id)
                .all()
            )
        except SQLAlchemyError as e:
            logger.error(f"查询房间住宿学生时发生错误: {str(e)}")
            raise HTTPException(status_code=500, detail="查询失败")
        
    def find_optimal_room(
        self,
        students: List[Student],
        preferred_floor: Optional[int] = None,
        preferred_building: Optional[int] = None
    ) -> Optional[Room]:
        """
        为一组学生找到最优的宿舍房间
        
        参数:
            students: 待分配的学生列表
            preferred_floor: 偏好的楼层
            preferred_building: 偏好的楼栋
        """
        if not students:
            return None
            
        required_capacity = len(students)
        gender = students[0].gender
        
        # 获取所有可用房间
        available_rooms = self.get_available_rooms(
            gender,
            required_capacity,
            preferred_floor,
            preferred_building
        )
        
        # 优先选择空房间
        empty_rooms = [r for r in available_rooms if r.current_occupancy == 0]
        if empty_rooms:
            return empty_rooms[0]
            
        # 如果没有空房间，选择剩余床位最多的房间
        available_rooms.sort(key=lambda r: r.capacity - r.current_occupancy, reverse=True)
        return available_rooms[0] if available_rooms else None
        
    def optimize_room_assignments(self) -> List[Tuple[Student, Room]]:
        """优化当前的宿舍分配情况"""
        try:
            # 获取所有需要调整的学生（当前没有宿舍的学生）
            unassigned_students = (
                self.db.query(Student)
                .filter(Student.room_id.is_(None))
                .all()
            )
            
            # 按性别分组
            male_students = [s for s in unassigned_students if s.gender == "male"]
            female_students = [s for s in unassigned_students if s.gender == "female"]
            
            assignments = []
            
            # 处理男生宿舍分配
            while male_students:
                current_group = male_students[:4]  # 每个宿舍最多4人
                male_students = male_students[4:]
                
                optimal_room = self.find_optimal_room(current_group)
                if optimal_room:
                    if self.assign_group_to_room(current_group, optimal_room):
                        assignments.extend([(s, optimal_room) for s in current_group])
                    
            # 处理女生宿舍分配
            while female_students:
                current_group = female_students[:4]
                female_students = female_students[4:]
                
                optimal_room = self.find_optimal_room(current_group)
                if optimal_room:
                    if self.assign_group_to_room(current_group, optimal_room):
                        assignments.extend([(s, optimal_room) for s in current_group])
                    
            return assignments
            
        except SQLAlchemyError as e:
            self.db.rollback()
            logger.error(f"优化宿舍分配时发生错误: {str(e)}")
            raise HTTPException(status_code=500, detail="优化宿舍分配失败")

    def create_swap_request(
        self,
        requester_id: int,
        target_id: int
    ) -> SwapRequest:
        """
        创建调换申请
        
        参数:
            requester_id: 申请人ID
            target_id: 目标学生ID
        """
        try:
            # 检查学生是否存在
            requester = self.db.query(Student).get(requester_id)
            target = self.db.query(Student).get(target_id)
            
            if not (requester and target):
                raise HTTPException(status_code=404, detail="找不到相关学生信息")
                
            # 检查是否已有宿舍
            if not (requester.room_id and target.room_id):
                raise HTTPException(status_code=400, detail="部分学生没有宿舍分配")
                
            # 检查性别
            if requester.gender != target.gender:
                raise HTTPException(status_code=400, detail="不同性别学生不能交换宿舍")
                
            # 检查是否已存在未处理的申请
            existing_request = (
                self.db.query(SwapRequest)
                .filter(
                    SwapRequest.requester_id == requester_id,
                    SwapRequest.target_id == target_id,
                    SwapRequest.status == "pending"
                )
                .first()
            )
            
            if existing_request:
                return existing_request
                
            # 创建新申请
            swap_request = SwapRequest(
                requester_id=requester_id,
                target_id=target_id,
                status="pending",
                created_at=datetime.now()
            )
            
            self.db.add(swap_request)
            self.db.commit()
            self.db.refresh(swap_request)
            return swap_request
            
        except SQLAlchemyError as e:
            self.db.rollback()
            logger.error(f"创建调换申请时发生错误: {str(e)}")
            raise HTTPException(status_code=500, detail="创建调换申请失败")

    def process_swap_request(
        self,
        request_id: int,
        admin_id: int,
        approve: bool
    ) -> bool:
        """
        处理调换申请
        
        参数:
            request_id: 申请ID
            admin_id: 处理管理员ID
            approve: 是否批准
        """
        try:
            # 获取申请信息
            swap_request = self.db.query(SwapRequest).get(request_id)
            if not swap_request:
                raise HTTPException(status_code=404, detail="找不到调换申请")
                
            if swap_request.status != "pending":
                raise HTTPException(status_code=400, detail="该申请已经处理过")
                
            # 获取相关学生信息
            requester = self.db.query(Student).get(swap_request.requester_id)
            target = self.db.query(Student).get(swap_request.target_id)
            
            if not (requester and target):
                raise HTTPException(status_code=404, detail="找不到相关学生信息")
                
            # 更新申请状态
            swap_request.status = "approved" if approve else "rejected"
            swap_request.processed_at = datetime.now()
            swap_request.processed_by = admin_id
            
            # 如果批准，执行交换
            if approve:
                self.swap_rooms(requester, target)
                
            self.db.commit()
            return True
            
        except SQLAlchemyError as e:
            self.db.rollback()
            logger.error(f"处理调换申请时发生错误: {str(e)}")
            raise HTTPException(status_code=500, detail="处理调换申请失败")

    def get_room_statistics(self, room_id: int) -> Dict:
        """
        获取房间统计信息
        
        参数:
            room_id: 房间ID
        """
        try:
            room = self.db.query(Room).get(room_id)
            if not room:
                raise HTTPException(status_code=404, detail="找不到房间信息")
                
            occupants = self.get_room_occupants(room_id)
            
            return {
                "room": room,
                "occupants": occupants,
                "occupancy_rate": room.current_occupancy / room.capacity,
                "available_beds": room.capacity - room.current_occupancy
            }
            
        except SQLAlchemyError as e:
            logger.error(f"获取房间统计信息时发生错误: {str(e)}")
            raise HTTPException(status_code=500, detail="获取统计信息失败")

    def get_building_statistics(self, building_id: int) -> Dict:
        """
        获取楼栋统计信息
        
        参数:
            building_id: 楼栋ID
        """
        try:
            building = self.db.query(Building).get(building_id)
            if not building:
                raise HTTPException(status_code=404, detail="找不到楼栋信息")
                
            # 使用聚合查询获取统计信息
            stats = (
                self.db.query(
                    func.count(Room.id).label("total_rooms"),
                    func.sum(Room.capacity).label("total_capacity"),
                    func.sum(Room.current_occupancy).label("current_occupancy")
                )
                .filter(Room.building_id == building_id)
                .first()
            )
            
            return {
                "building": building,
                "total_rooms": stats.total_rooms,
                "total_capacity": stats.total_capacity,
                "current_occupancy": stats.current_occupancy,
                "occupancy_rate": stats.current_occupancy / stats.total_capacity if stats.total_capacity else 0,
                "available_beds": stats.total_capacity - stats.current_occupancy
            }
            
        except SQLAlchemyError as e:
            logger.error(f"获取楼栋统计信息时发生错误: {str(e)}")
            raise HTTPException(status_code=500, detail="获取统计信息失败") 